﻿#include "fcamera.h"

#include <QDebug>

FCamera::FCamera(QObject *parent) : QtFfmpegCore(parent)
{

}

bool FCamera::openDev(const QString &url)
{
    QStringList options = getCameraOptions(url);
    qDebug()<<"option = "<<options;

    bool ret = initCamera(url);
    if (ret == false){
        freeDev();
        return false;
    }

    ret = initVideoDecoder();
    if (ret == false){
        freeDev();
        return false;
    }

    return true;
}

void FCamera::closeDev()
{

}

bool FCamera::initCamera(const QString &dev)
{
    //配置摄像头参数
    AVDictionary * options = nullptr;
    av_dict_set(&options, "input_format", "mjpeg", 0);
    av_dict_set(&options, "framerate", "30", 0);
    av_dict_set(&options, "video_size", "1920x1080", 0);

    m_formatCtx = avformat_alloc_context();
    if (m_formatCtx == nullptr){
        av_log(nullptr, AV_LOG_ERROR, "Alloc context failed\n");
        return false;
    }

    //查询系统支持输入设备
    const AVInputFormat * inFmt = nullptr;
    inFmt = av_find_input_format(AV_INPUT_FORMAT);
    if (inFmt == nullptr){
        av_log(nullptr, AV_LOG_ERROR, "Font input format failed\n");
        return false;
    }

    //打开摄像头设备
    int ret = avformat_open_input(&m_formatCtx, dev.toLocal8Bit().data(), inFmt, &options);
    if (options){
        //释放字典内存
        av_dict_free(&options);
        options = nullptr;
    }
    if (ret < 0){
        av_log(nullptr, AV_LOG_ERROR, "Input file open failed\n");
        return false;
    }

    //查看数据流
    ret = avformat_find_stream_info(m_formatCtx, nullptr);
    if (ret < 0){
        av_log(nullptr, AV_LOG_ERROR, "Find camera stream inform failed\n");
        return false;
    }
    av_log(nullptr, AV_LOG_FATAL, "Open camera %s success\n", dev.toLocal8Bit().data());

    return true;
}

bool FCamera::initVideoDecoder()
{
    m_videoIndex = av_find_best_stream(m_formatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (m_videoIndex < 0){
        av_log(nullptr, AV_LOG_ERROR, "Find video stream failed\n");
        return false;
    }
    AVStream * videoStream = m_formatCtx->streams[m_videoIndex];
    m_imageSize.setWidth(videoStream->codecpar->width);
    m_imageSize.setHeight(videoStream->codecpar->height);
    m_frameRate = rationalToDouble(&videoStream->avg_frame_rate);

    //通过解码器ID来获取视频解码器
    const AVCodec * codec = avcodec_find_decoder(videoStream->codecpar->codec_id);
    m_codecCtx = avcodec_alloc_context3(codec);
    if (m_codecCtx == nullptr){
        av_log(nullptr, AV_LOG_ERROR, "Avcodec context alloc failed\n");
        return false;
    }

    //根据视频流信息来初始化解码器上下文参数
    int ret = avcodec_parameters_to_context(m_codecCtx, videoStream->codecpar);
    if (ret < 0){
        av_log(nullptr, AV_LOG_ERROR, "Avcodec context parameter init failed\n");
        return false;
    }
    m_codecCtx->flags2 |= AV_CODEC_FLAG2_FAST;  //允许不符合规范的加速技巧
    m_codecCtx->thread_count = 8;               //使用8线程解码

    //打开解码器,初始化解码器上下文，如果之前avcodec_alloc_context3传入了解码器，第二个参数设置NULL就可以
    ret = avcodec_open2(m_codecCtx, nullptr, nullptr);
    if (ret < 0){
        av_log(nullptr, AV_LOG_ERROR, "Avcodec open failed\n");
        return false;
    }

    return true;
}

bool FCamera::initBuffer()
{
    m_packet = av_packet_alloc();
    if (m_packet == nullptr){
        return false;
    }

    //解码后的帧数据
    m_frame = av_frame_alloc();
    if (m_frame == nullptr){
        return false;
    }
    int size = av_image_get_buffer_size(AV_PIX_FMT_RGBA, m_imageSize.width(), m_imageSize.height(), 4);
    uint8_t * buff = static_cast<uint8_t *>(av_malloc(size_t(size)));

    int ret = av_image_fill_arrays(m_frame->data, m_frame->linesize, buff, AV_PIX_FMT_RGBA, m_imageSize.width(), m_imageSize.height(), 1);
    if (ret < 0){
        return false;
    }

    return true;
}

void FCamera::freeDev()
{
    if (m_codecCtx){
        avcodec_free_context(&m_codecCtx);
    }

    if (m_formatCtx){
        avformat_close_input(&m_formatCtx);
    }

    if (m_packet){
        av_packet_free(&m_packet);
    }

    if (m_frame){
        uint8_t ** buff = m_frame->data;
        if (buff){
            delete [] buff;
            buff = nullptr;
        }
        av_frame_free(&m_frame);
    }
}
